﻿using System;
using System.Collections.Generic;

namespace EasyConveyor
{
	/// <summary>
	/// A class representing a builder for a <see cref="Pipeline"/>.
	/// </summary>
	public sealed class PipelineBuilder
	{
		private readonly List<Type> _middleWare = new List<Type>();

		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="workerThreadCount">The number of threads to use for this pipeline configuration. This is also the number of concurrent instances of the pipeline built by this class.</param>
		/// <param name="activator">An instance of an <see cref="ActivatorBase"/>. The worker threads will request work from the activator.</param>
		/// <param name="logger">A delegate <see cref="Action{T}"/> that accepts a <see cref="string"/> that will be invoked when a message should be logged.</param>
		/// <exception cref="ArgumentNullException">Thrown when the specified <paramref name="activator"/> is null.</exception>
		/// <exception cref="ArgumentException">Thrown when the specified <paramref name="workerThreadCount"/> is zero. <paramref name="workerThreadCount"/> must be a positive integer.</exception>
		public PipelineBuilder(ushort workerThreadCount, ActivatorBase activator, Action<string> logger)
		{
			if (activator == null)
				throw new ArgumentNullException("activator");
			if (workerThreadCount == 0)
				throw new ArgumentException("Value may not be 0.", "workerThreadCount");

			Logger = logger;

			WorkerThreadCount = workerThreadCount;
			Activator = activator;
		}

		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="workerThreadCount">The number of threads to use for this pipeline configuration. This is also the number of concurrent instances of the pipeline built by this class.</param>
		/// <param name="activator">An instance of an <see cref="ActivatorBase"/>. The worker threads will request work from the activator.</param>
		public PipelineBuilder(ushort workerThreadCount, ActivatorBase activator)
			: this(workerThreadCount, activator, null)
		{
		}

		/// <summary>
		/// Gets the number of threads to use for this pipeline configuration. This is also the number of concurrent instances of the pipeline built by this class.
		/// </summary>
		public ushort WorkerThreadCount { get; private set; }

		/// <summary>
		/// Sets the delegate <see cref="Action{T}"/> that accepts a <see cref="string"/> that will be invoked when a message should be logged.
		/// </summary>
		public Action<string> Logger { private get; set; }

		internal ActivatorBase Activator { get; private set; }

		/// <summary>
		/// Adds a middleware type to the list of middleware types that will be invoked for each request. The order in which these types are added is the order in which they will be invoked.
		/// </summary>
		/// <param name="middlewareType">The type, which must be assignable to <see cref="IMiddleware"/>, of the middleware to instantiate and invoke.</param>
		/// <exception cref="ArgumentException">Thrown when the specified <paramref name="middlewareType"/> is not assignable to <see cref="IMiddleware"/>.</exception>
		public void AddMiddleware(Type middlewareType)
		{
			if (middlewareType == null) return;
			if (!typeof(IMiddleware).IsAssignableFrom(middlewareType))
				throw new ArgumentException("The specified type must be of type 'IMiddleware'");

			_middleWare.Add(middlewareType);
		}

		internal IEnumerable<Pipeline> Build()
		{
			for (ushort i = 0; i < WorkerThreadCount; i++)
			{
				Activator.IsAttachedToPipeline = true;
				yield return new Pipeline(Activator, _middleWare, Logger);
			}
		}
	}
}
